<template>
  <div class="date-picker">
    <input
      class="result"
      readonly
      type="text"
      :value="dateValue"
      @focus="showDropdown = true"
    >
    <transition name="slide-up">
      <div v-show="showDropdown" class="dropdown">
        <!-- 日期面板 -->
        <div v-show="showPanel === 'date'">
          <header class="date-header">
            <i class="fas fa-angle-double-left" @click="changeYearOrMonth('year', -1)" />
            <i class="fas fa-angle-left" @click="changeYearOrMonth('month', -1)" />
            <strong class="year" @click="showYearPanel">{{ curYear }}</strong>
            <strong class="month" @click="showPanel = 'month'">{{ MONTH_NAME[curMonth] }}</strong>
            <i class="fas fa-angle-right" @click="changeYearOrMonth('month', 1)" />
            <i class="fas fa-angle-double-right" @click="changeYearOrMonth('year', 1)" />
          </header>
          <ul class="date-week">
            <li v-for="item of week" :key="item">
              {{ item }}
            </li>
          </ul>
          <ul class="date-list">
            <li
              v-for="item of dateList"
              :key="item.value"
              :class="{ 'no-current': item.type !== 'current', 'active': item.value === selectedDay }"
              @click="handleDayClick(item)"
            >
              {{ item.day }}
            </li>
          </ul>
        </div>
        <!-- 年份面板 -->
        <ul v-show="showPanel === 'year'" class="year-panel">
          <li
            v-for="item of yearList"
            :key="item"
            :class="['year-panel-item', { active: curYear === item }]"
            @click="curYear = item; showPanel = 'date'"
          >
            {{ item }}
          </li>
        </ul>
        <!-- 月份面板 -->
        <ul v-show="showPanel === 'month'" class="month-panel">
          <li
            v-for="item of MONTH_NAME"
            :key="item"
            :class="['month-panel-item', { active: MONTH_NAME.indexOf(item) === curMonth }]"
            @click="curMonth = MONTH_NAME.indexOf(item); showPanel = 'date'"
          >
            {{ item.slice(0, 3) }}
          </li>
        </ul>
      </div>
    </transition>
  </div>
</template>

<script lang="ts" setup>
import { computed, nextTick, ref, watch, onBeforeUnmount } from 'vue'
import { DateInfo } from '../types/date'

const currentDate = new Date()
const yearList = ref<number[]>([]) // 年份列表
const dateList = ref<DateInfo[]>([])
const curYear = ref(currentDate.getFullYear())
const curMonth = ref(currentDate.getMonth())
// const curDay = ref(currentDate.getDate())
const showPanel = ref('date')
const showDropdown = ref(false)
const selectedDay = ref<number>()

const week = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']
const MONTH_NAME = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']

// 监听鼠标是否点击外部
;(function () {
  const handler = (e: MouseEvent) => {
    const datePickerEl = document.getElementsByClassName('date-picker')[0]
    if (!datePickerEl.contains(e.target as Node)) showDropdown.value = false
  }
  document.addEventListener('click', handler)

  onBeforeUnmount(() => {
    document.removeEventListener('click', handler)
  })
})()

const dateValue = computed<string>(() => {
  if (!selectedDay.value) return ''

  const d = new Date(selectedDay.value)
  const y = d.getFullYear()
  const m = d.getMonth().toString().padStart(2, '0')
  const day = d.getDate().toString().padStart(2, '0')

  return `${y}年${m}月${day}日`
})

// 生成年份列表
;(function () {
  for (let i = 1970; i < curYear.value + 100; i++) {
    yearList.value.push(i)
  }
})()

// 获取传入的月份有多少天
const getDayLength = (year: number, month: number): number => {
  return new Date(year, month + 1, 0).getDate()
}

// 设置日期列表
function setDateList (year: number, month: number) {
  const curDays = getDayLength(year, month) // 当月天数
  const prevDays = getDayLength(year, month - 1) // 上月天数
  const curFirstDayWeek = new Date(year, month, 1).getDay() // 当月第一天星期几

  const list: DateInfo[] = []

  // 填充上月最后几天
  for (let i = prevDays - curFirstDayWeek + 1; i <= prevDays; i++) {
    list.push({
      day: i,
      value: +new Date(year, month - 1, i),
      isRange: false,
      isSelected: false,
      type: 'prev'
    })
  }

  // 填充当月
  for (let i = 1; i <= curDays; i++) {
    list.push({
      day: i,
      value: +new Date(year, month, i),
      isRange: false,
      isSelected: false,
      type: 'current'
    })
  }

  const nextDays = 7 - (list.length % 7)
  if (nextDays !== 7) {
    // 填充下月
    for (let i = 1; i <= nextDays; i++) {
      list.push({
        day: i,
        value: +new Date(year, month + 1, i),
        isRange: false,
        isSelected: false,
        type: 'next'
      })
    }
  }

  dateList.value = list
}

watch(
  [curYear, curMonth],
  () => {
    setDateList(curYear.value, curMonth.value)
  },
  { immediate: true }
)

// 点击日期
function handleDayClick (item: DateInfo) {
  selectedDay.value = item.value

  if (item.type !== 'current') {
    curMonth.value = item.type === 'prev' ? curMonth.value - 1 : curMonth.value + 1
  }

  showDropdown.value = false
}

// 切换年月
function changeYearOrMonth (type: 'year' | 'month', num: number) {
  if (type === 'year') {
    curYear.value += num
  } else {
    let month = curMonth.value + num

    if (month > 11) {
      month = 0
      curYear.value++
    } else if (month < 0) {
      month = 11
      curYear.value--
    }

    curMonth.value = month
  }
}

// 显示年列表面板
function showYearPanel () {
  showPanel.value = 'year'
  nextTick(() => {
    (document.querySelector('.year-panel-item.active') as HTMLElement).scrollIntoView({ block: 'center' })
  })
}
</script>

<style lang="stylus">
color-primary = #496cf6

.date-picker
  position relative
  width 200px
  height 30px
  margin  0 auto

  .result
    width 100%
    height 100%
    padding 0 10px
    border 1px solid #dedede
    border-radius 4px
    outline none
    font-size 14px
    transition .3s

    &:hover
      border-color #bdbdbd

    &:focus
      border-color color-primary

  .dropdown
    position absolute
    top 100%
    left 50%
    width 300px
    padding 16px 8px
    margin-top 12px
    margin-left -(@width / 2)
    border-radius 4px
    background #fff
    filter drop-shadow(2px 2px 8px rgba(0, 0, 0, .1))
    //transform translate3d(-50%, 0, 0)
    //transform translateX(-50%)

    &::before
      content ""
      position absolute
      top -8px
      left 50%
      margin-left -8px
      border-width 0 8px 8px 8px
      border-style solid
      border-color transparent transparent #fff transparent

.date-header
  display flex
  align-items center

  *
    cursor pointer

    &:hover
      color color-primary

  .fa-angle-left
  .month
  .fa-angle-double-right
    margin-left 10px

  .year
    margin-left auto

  .month
    margin-right auto

.date-week
  display flex
  align-items center
  margin-top 30px

  li
    flex 1
    font-size 12px
    text-align center

.date-list
  display flex
  flex-wrap wrap
  margin-top 8px

  li
    width calc(100% / 7)
    height 40px
    border-radius 50%
    line-height @height
    text-align center
    cursor pointer

    &.active
      color #fff
      background color-primary

  .no-current
    color #c4c4c4

.year-panel
  max-height 300px
  overflow-y auto

  &-item
    height 30px
    line-height @height
    cursor pointer
    transition .15s ease-in-out

    &.active
      font-weight bold
      color color-primary

    &:hover
      background #e8e8e8

.month-panel
  display grid
  grid-template-columns repeat(3, 1fr)

  &-item
    text-align center
    height 50px
    line-height @height
    cursor pointer
    transition .15s ease-in-out

    &:hover
      background #e8e8e8

    &.active
      font-weight bold
      color color-primary

.slide-up
  &-enter-active
  &-leave-active
    transition .2s ease-in-out

  &-enter-from
  &-leave-to
    transform translate3d(0, -20px, 0)
    opacity 0
</style>
